/*
 * Copyright 2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package xyz.zhouxy.oauth2.client;

import org.springframework.web.bind.annotation.RestController;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpLogic;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import xyz.zhouxy.plusone.commons.model.dto.UnifiedResponse;
import xyz.zhouxy.plusone.commons.model.dto.UnifiedResponses;

import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RestController
public class LoginController {

    OkHttpClient http = new OkHttpClient();

    private String clientId = "6e8f83dd8076bb781645dfab59fc26393b182e2a558848c741e7ce3454173cea";

    private String clientSecret = "1d3d4efe3e560adef4fe0141d37350f42422ea831ec9e9bf4aed73d579000f7f";

    private final ObjectMapper jackson;

    @Autowired
    private SysAccountService accountService;

    private StpLogic stpLogic = AuthLogic.userAuthLogic;

    LoginController() {
        jackson = new ObjectMapper();
        jackson.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        jackson.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    @GetMapping("doLogin")
    public UnifiedResponse<LoginResponse> loginByOAuth2Code(@RequestParam String code) throws IOException {
        GiteeTokenResult result = giteeOAuth2Token(code);
        GiteeUserInfo userInfo = giteeOAuth2UserInfo(result.getAccessToken());
        SysAccountInfo sysAccountInfo = accountService.querySysAccountInfoByGiteeId(Long.toString(userInfo.getId()));
        Long accountId = sysAccountInfo.getId();
        stpLogic.login(accountId);

        SaSession session = stpLogic.getSessionByLoginId(accountId);
        session.set("userInfo", sysAccountInfo);
        return UnifiedResponses.success("success", new LoginResponse(stpLogic.getTokenValue(), sysAccountInfo));
    }

    private RequestBody buildGetTokenRequestBody(String code, String redirectUri) {
        final MediaType mediaType = MediaType.parse("multipart/form-data; charset=utf-8");
        final String params = "grant_type=authorization_code&code=%s&client_id=%s&redirect_uri=%s&client_secret=%s";
        final String content = String.format(params, code, clientId, redirectUri, clientSecret);
        return RequestBody.create(content, mediaType);
    }

    private GiteeTokenResult giteeOAuth2Token(String code) throws IOException {
        Request request = new Request.Builder()
                .url("https://gitee.com/oauth/token")
                .post(buildGetTokenRequestBody(code, "http://localhost:5173/login"))
                .build();
        Response resp = http.newCall(request).execute();
        ResponseBody body = resp.body();
        return jackson.readValue(body.string(), GiteeTokenResult.class);
    }

    private GiteeUserInfo giteeOAuth2UserInfo(String accessToken) throws IOException {
        Request request = new Request.Builder()
                .url("https://gitee.com/api/v5/user?access_token=" + accessToken)
                .get()
                .build();
        Response resp = http.newCall(request).execute();
        ResponseBody body = resp.body();
        return jackson.readValue(body.string(), GiteeUserInfo.class);
    }

}

@Data
class GiteeTokenResult {
    private String accessToken;
    private String tokenType;
    private Long expiresIn;
    private String refreshToken;
    private String scope;
    private Long createdAt;
}

@Data
class GiteeUserInfo {
    private Long id;
    private String login;
    private String name;
    private String avatarUrl;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class SysAccountInfo {
    Long id;
    String name;
    String email;

    String giteeId;
}

@Service
class SysAccountService {
    public SysAccountInfo querySysAccountInfoByGiteeId(String giteeId) {
        return new SysAccountInfo(108L, "zhouxy108", "luquanlion@outlook.com", giteeId);
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class LoginResponse {
    String token;
    SysAccountInfo accountInfo;
}

class AuthLogic {
    private AuthLogic() {
        throw new IllegalStateException("Utility class");
    }

    public static final String ADMIN_LOGIN_TYPE = "Admin";
    public static final StpLogic adminAuthLogic = new StpLogic(ADMIN_LOGIN_TYPE);

    public static final String USER_LOGIN_TYPE = "User";
    public static final StpLogic userAuthLogic = new StpLogic(USER_LOGIN_TYPE);
}
